#pragma once

#include "numeric.h"
#include "symbol.h"
#include <vector>

#ifdef GRAPHENETOOLS_EXPORTS
#ifndef GRAPHENETOOLS_DLL_API
#define GRAPHENETOOLS_DLL_API __declspec(dllexport)
#endif
#else
#ifndef GRAPHENETOOLS_DLL_API
#define GRAPHENETOOLS_DLL_API __declspec(dllimport)
#endif
#endif

class GRAPHENETOOLS_DLL_API variant
{
public:

	enum type{
		empty,
		booltype,
		numerictype,
		rangetype,
		stringtype,
		symboltype,
		numericlisttype,
		listtype
	};

	typedef std::vector<variant> list;

private:
	type valuetype;

	union {
		bool boolvalue;
		numeric* numericvalue;
		numeric::range* rangevalue;
		string* stringvalue;
		symbol* symbolvalue;
		list* listvalue;
		numeric::list* numericlistvalue;
		void* dummypointer;
	};

public:
	variant();						// Default constructor creates an empty variant.
	variant(const variant &v);		// Copy constructor creates an identical variant.
	variant(const bool &v);			// Conversion constructor creates a booltype variant.
	variant(const size_t &v);		// Conversion constructor creates a numerictype variant.
	variant(const long &v);			// Conversion constructor creates a numerictype variant.
	variant(const double &v);		// Conversion constructor creates a numerictype variant.
	variant(const complex &v);		// Conversion constructor creates a numerictype variant.
	variant(const numeric &v);		// Conversion constructor creates a numerictype variant.
	variant(const numeric::range &v);	// Conversion constructor creates a rangetype variant.
	variant(const string &v);		// Conversion constructor creates a stringtype variant.
	variant(const symbol &v);		// Conversion constructor creates a symboltype variant.
	variant(const numeric::list &v);// Conversion constructor creates a numericlisttype variant.
	variant(const list &v);			// Conversion constructor creates a listtype variant. Note lists of only numerics are converted to numericlists.

	//std::vector conversion constructors.
	
	variant(const std::vector<size_t> &v);		// Conversion constructor creates a numericlisttype variant.
	variant(const std::vector<long> &v);		// Conversion constructor creates a numericlisttype variant.
	variant(const std::vector<double> &v);		// Conversion constructor creates a numericlisttype variant.
	variant(const std::vector<complex> &v);		// Conversion constructor creates a numericlisttype variant.

	//Armadillo conversion constructors.
	
	variant(const arma::vec2 &v);		// Conversion constructor creates a numericlisttype variant.
	variant(const arma::vec3 &v);		// Conversion constructor creates a numericlisttype variant.
	variant(const arma::ivec2 &v);		// Conversion constructor creates a numericlisttype variant.
	variant(const arma::ivec3 &v);		// Conversion constructor creates a numericlisttype variant.
	variant(const arma::ivec4 &v);		// Conversion constructor creates a numericlisttype variant.
	variant(const arma::vec &v);		// Conversion constructor creates a numericlisttype variant.
	variant(const arma::ivec &v);		// Conversion constructor creates a numericlisttype variant.
	variant(const arma::uvec &v);		// Conversion constructor creates a numericlisttype variant.
	variant(const arma::cx_vec &v);		// Conversion constructor creates a numericlisttype variant.
	variant(const arma::mat &v);		// Conversion constructor creates a numericlisttype variant.
	variant(const arma::imat &v);		// Conversion constructor creates a numericlisttype variant.
	variant(const arma::cx_mat &v);		// Conversion constructor creates a numericlisttype variant.
	
	variant &operator=(const variant &v);// Assignment operator creates an identical variant.
	
	~variant();						// Destructor frees all used memory.
	
	void clear();					// Frees all used memory.

	type getType() const;			// Type getter function.
	bool isEmpty() const;			// Tests for empty type, same as getType() == variant::empty.
	bool isBoolType() const;		// Tests for bool type, same as getType() == variant::booltype.
	bool isNumericType() const;		// Tests for numeric type, same as getType() == variant::numerictype.
	bool isRangeType() const;		// Tests for numericrange type, same as getType() == variant::rangetype.
	bool isStringType() const;		// Tests for string type, same as getType() == variant::stringtype.
	bool isSymbolType() const;		// Tests for symbol type, same as getType() == variant::symboltype.
	bool isNumericListType() const;	// Tests for variantlist type, same as getType() == variant::numericlisttype.
	bool isListType() const;		// Tests for variantlist type, same as getType() == variant::listtype.
	
	bool toBool() const;				// Explicit conversion to bool.
	numeric toNumeric() const;			// Explicit conversion to numeric.
	size_t toSize() const;				// Explicit conversion to size_t.
	long toLong() const;				// Explicit conversion to long.
	double toDouble() const;			// Explicit conversion to double.
	complex toComplex() const;			// Explicit conversion to complex.
	numeric::range toRange() const;		// Explicit conversion to numericrange.
	string toString() const;			// Explicit conversion to string.
	symbol toSymbol() const;			// Explicit conversion to symbol.
	numeric::list toNumericList() const;// Explicit conversion to numericlist.
	list toList() const;				// Explicit conversion to variantlist.
	
	// Explicit std::vector conversion.
	
	std::vector<size_t> toStdSizeVector() const;	// Explicit conversion to std::vector.
	std::vector<long> toStdLongVector() const;		// Explicit conversion to std::vector.
	std::vector<double> toStdDoubleVector() const;	// Explicit conversion to std::vector.
	std::vector<complex> toStdComplexVector() const;// Explicit conversion to std::vector.
	std::vector<numeric> toStdNumericVector() const;// Explicit conversion to std::vector.

	// Explicit Armadillo conversion.
	
	arma::vec2 toArmaDoubleVector2() const;		// Explicit conversion to arma::vec.
	arma::vec3 toArmaDoubleVector3() const;		// Explicit conversion to arma::vec.
	arma::ivec2 toArmaLongVector2() const;		// Explicit conversion to arma::vec.
	arma::ivec3 toArmaLongVector3() const;		// Explicit conversion to arma::vec.
	arma::ivec4 toArmaLongVector4() const;		// Explicit conversion to arma::vec.
	arma::vec toArmaDoubleVector() const;		// Explicit conversion to arma::vec.
	arma::ivec toArmaLongVector() const;		// Explicit conversion to arma::vec.
	arma::uvec toArmaSizeVector() const;		// Explicit conversion to arma::vec.
	arma::cx_vec toArmaComplexVector() const;	// Explicit conversion to arma::vec.
};

struct rule : public std::pair<variant,variant>
{
	typedef std::vector<rule> list;
};